#ifndef __INCLUDE_GUARD_0FEF5793_A843_48DE_8050_1137AEAD3804
#define __INCLUDE_GUARD_0FEF5793_A843_48DE_8050_1137AEAD3804
#ifdef _MSC_VER
	#pragma once
#endif

#include "../core/image.h"
#include "basic_definitions.h"

#include "bvh.h"
#include "kd.h"

#define DOF 1

//A sampler telling how to sample a pixel
struct Sampler : public RefCntBase
{
	//A sample is a poisition [0..1]x[0..1] within the pixel
	//	as well as a weight, telling how much this sample
	//	contributes to the final value of the pixel
	struct Sample
	{
		float2 position;
		float weight;
	};

	//Pushes all samples to _result
	virtual void getSamples(uint _x, uint _y, std::vector<Sample> &_result) = 0;
};

struct Plane 
{
	float4 equation;

	Plane(Point _origin, Vector _normal) 
	{
		equation = _normal;
		equation.w = -equation.dot(_origin);
	}

	virtual float intersect(const Ray& _ray ) const
	{
		float ret=-1;

		float div = float4(_ray.d).dot(equation);
		if(fabs(div) >0.00001)
		{
			float dist = -float4(_ray.o).dot(equation) / div;
			ret = dist;
		}

		return ret;
	}
};

//A renderer class
class Renderer
{
public:
	SmartPtr<Sampler> sampler;
	SmartPtr<Camera> camera;
	SmartPtr<Integrator> integrator;
	SmartPtr<Image> target;

	void render(BVH baum)
	{
		int pro = 0;
		
		//Loop through all pixels in the scene and determine their color
		//	from the integrator
#pragma omp parallel 
		{
			//Ray foc(camera->getCenter(), camera->forward);
			Plane focalplane(camera->focalpoint, ~(-camera->forward));

			std::vector<Sampler::Sample> samples;
#pragma omp for schedule(dynamic, 10)
			for(int y = 0; y < (int)target->height(); y++)
			{
				if(( (float)(y)/(float)(target->height()) *10.0f) == 1.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 2.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 3.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 4.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 5.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 6.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 7.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 8.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 9.f
					||( (float)(y)/(float)(target->height()) *10.0f) == 10.f)
				{
					pro += 10;
					std::cout<<pro<<"%"<<std::endl;
				}
				for(int x = 0; x < (int)target->width(); x++)
				{
					float4 color = float4::rep(0.f);

					samples.clear();
					sampler->getSamples((uint)x, (uint)y, samples);

					//Accumulate the samples
					for(size_t i = 0; i < samples.size(); i++)
					{
						Ray r = camera->getPrimaryRay(samples[i].position.x + x, samples[i].position.y + y, (float)rand() / RAND_MAX);//time:random betwenn 0,1 for MotionBlur

						if(DOF==1){
							//calculate the intersection-point between the focalplane and the ray
							Point p = r.getPoint(focalplane.intersect(r));
							//to shift the origin of the ray, create randoms
							float ranx = 2.f*((float)rand()/(float)RAND_MAX)-1.f, // in [-1,1]
								rany = 2.f*((float)rand()/(float)RAND_MAX)-1.f,	// in [-1,1]
								ranz = 2.f*((float)rand()/(float)RAND_MAX)-1.f;	// in [-1,1]
							//shift the origin by a random value of the lenssize
							r.o = Point(r.o.x + ranx*camera->lenssize, r.o.y + rany*camera->lenssize, r.o.z + ranz*camera->lenssize);
							//compute the new direction-vector, from shifted origin to intersection-point p
							r.d = ~(p-r.o);
						}

						//rays are initially in air
						
						r.density = 1.0003f;

						color += integrator->getRadiance(r) * float4::rep(samples[i].weight);
					}

					(*target)(x, y) = color;
					
				}
			}

		}
	}
};


#endif //__INCLUDE_GUARD_0FEF5793_A843_48DE_8050_1137AEAD3804
